//
// Created by WangPeng on 24-8-16.
//

#include "h264_file.h"
#include "../rtsp_protocol/rtp_protocol.h"

#include <utility>
#include <iostream>
#include <unistd.h>
#include <cstdint>
#include <cstring>

CH264File::CH264File(std::string file_name) : CAbstractInterface(std::move(file_name))
{
    ifs_.open(file_name_, std::ios::binary);
    if (!ifs_)
    {
        std::cerr << "the file {" << file_name_ << "} open fail!" << std::endl;
        return;
    }
}

CH264File::~CH264File()
{
    ifs_.close();
}

long CH264File::get_frame_data(char *&frame, long size)
{
    if (!ifs_.read(frame, size))
    {
        return -1;
    }

    long read_size = ifs_.gcount();

    bool is_code3 = start_code3(frame);
    if (!is_code3 && !start_code4(frame))
        return -1;

    long frame_size;
    char* next_start_code = find_next_start_code(frame + 3, read_size - 3);
    if (!next_start_code)
    {
        return -1;
    }
    else
    {
        frame_size = (next_start_code - frame);
        ifs_.seekg(frame_size - read_size, std::ios::cur);
    }

    if (is_code3)
    {
        frame_size -= 3;
        frame += 3;
    }
    else
    {
        frame_size -= 4;
        frame += 4;
    }
    return frame_size;
}

int CH264File::start_code3(const char *buf)
{
    if (buf[0] == 0 && buf[1] == 0 && buf[2] == 1)
        return 1;
    else
        return 0;
}

int CH264File::start_code4(const char *buf)
{
    if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 1)
        return 1;
    else
        return 0;
}

char *CH264File::find_next_start_code(char *buf, long len)
{
    if (len < 3) return nullptr;

    for (int i = 0; i < len - 3; ++i)
    {
        if (start_code3(buf) || start_code4(buf)) return buf;
        ++buf;
    }

    if (start_code3(buf)) return buf;
    return nullptr;
}

void CH264File::play(CUdpSocket *udp_socket, CRtpProtocol* rtp_protocol)
{
    const long buffer_size = 500000;
    char* frame_buffer = (char*) malloc(buffer_size);
    while (true)
    {
        long frame_size = get_frame_data(frame_buffer, buffer_size);
        if (frame_size < 1) break;
        send_rtp_payload_over_udp(udp_socket, rtp_protocol, frame_buffer, frame_size);
        usleep(40000);
    }
}

int CH264File::send_rtp_payload_over_udp(CUdpSocket *udp_socket, CRtpProtocol* rtp_protocol, char *frame, std::size_t frame_size)
{
    int send_bytes = 0;
    int ret;

    uint8_t nalu_type = frame[0]; // nalu第一个字节

    std::cout << "frame_size =" << frame_size << std::endl;
    struct RtpPacket* packet = rtp_protocol->get_packet();
    if (frame_size <= RTP_MAX_PKT_SIZE) // nalu长度小于最大包长：单一NALU单元模式
    {

        //*   0 1 2 3 4 5 6 7 8 9
        //*  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        //*  |F|NRI|  Type   | a single NAL unit ... |
        //*  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        std::memcpy(packet->payload, frame, frame_size);
        ret = CRtpProtocol::send_rtp_over_udp(udp_socket, packet, frame_size);
        if(ret < 0) return -1;

        packet->hrtp.seq++;
        send_bytes += ret;
        if ((nalu_type & 0x1F) == 7 || (nalu_type & 0x1F) == 8) // 如果是SPS、PPS就不需要加时间戳
        {
            return send_bytes;
        }
    }
    else // nalu长度小于最大包场：分片模式
    {

        //*  0                   1                   2
        //*  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
        //* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        //* | FU indicator  |   FU header   |   FU payload   ...  |
        //* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+



        //*     FU Indicator
        //*    0 1 2 3 4 5 6 7
        //*   +-+-+-+-+-+-+-+-+
        //*   |F|NRI|  Type   |
        //*   +---------------+



        //*      FU Header
        //*    0 1 2 3 4 5 6 7
        //*   +-+-+-+-+-+-+-+-+
        //*   |S|E|R|  Type   |
        //*   +---------------+


        std::size_t pkt_num = frame_size / RTP_MAX_PKT_SIZE;       // 有几个完整的包
        std::size_t remain_pkt_size = frame_size % RTP_MAX_PKT_SIZE; // 剩余不完整包的大小
        int pos = 1;

        // 发送完整的包
        for (int i = 0; i < pkt_num; i++)
        {
            packet->payload[0] = (nalu_type & 0x60) | 28;
            packet->payload[1] = nalu_type & 0x1F;

            if (i == 0) //第一包数据
                packet->payload[1] |= 0x80; // start
            else if (remain_pkt_size == 0 && i == pkt_num - 1) //最后一包数据
                packet->payload[1] |= 0x40; // end

            std::memcpy(packet->payload + 2, frame + pos, RTP_MAX_PKT_SIZE);
            ret = CRtpProtocol::send_rtp_over_udp(udp_socket, packet, RTP_MAX_PKT_SIZE + 2);
            if(ret < 0) return -1;

            packet->hrtp.seq++;
            send_bytes += ret;
            pos += RTP_MAX_PKT_SIZE;
        }

        // 发送剩余的数据
        if (remain_pkt_size > 0)
        {
            packet->payload[0] = (nalu_type & 0x60) | 28;
            packet->payload[1] = nalu_type & 0x1F;
            packet->payload[1] |= 0x40; //end

            memcpy(packet->payload+2, frame + pos, remain_pkt_size + 2);
            ret = CRtpProtocol::send_rtp_over_udp(udp_socket, packet, remain_pkt_size + 2);
            if(ret < 0) return -1;

            packet->hrtp.seq++;
            send_bytes += ret;
        }
    }
    packet->hrtp.timestamp += 90000 / 25;
    return send_bytes;
}
